import { net } from 'electron';
import * as fs from 'fs';
import * as path from 'path';

// 获取文件扩展名
function getExt(url: string): string {
    const ext = url.slice(-5);
    const dotIndex = ext.indexOf(".");
    return dotIndex !== -1 ? ext.slice(dotIndex + 1) : "";
}

// 获取最后一个路径部分
function getLastPath(url: string): string {
    const lastSlashIndex = url.lastIndexOf("/");
    return lastSlashIndex !== -1 ? url.slice(lastSlashIndex) : "";
}

// 删除文件
function removeFile(targetPath: string): void {
    try {
        fs.rmSync(targetPath);
    } catch (error) {
        console.error(error);
    }
}

// 获取基础 URL
function getLastBase(url: string): string {
    const lastSlashIndex = url.lastIndexOf("/");
    return lastSlashIndex !== -1 ? url.slice(0, lastSlashIndex) : url;
}

// 获取完整的路径，Electron 主动过滤
function getBasePath(url: string): string {
    let pathEndIndex = url.lastIndexOf("/");
    if (pathEndIndex < 0) {
        pathEndIndex = 0;
    }
    const hashIndex = url.indexOf("#", pathEndIndex);
    if (hashIndex > -1) {
        url = url.substring(0, hashIndex);
    }
    const protocolSeparatorIndex = url.indexOf("://");
    if (protocolSeparatorIndex < 0) {
        return url; // 如果没有协议，返回原始 URL
    }
    const pathStartIndex = url.indexOf("/", protocolSeparatorIndex + 3);
    return pathStartIndex < 0
        ? url.slice(protocolSeparatorIndex + 3)
        : url.slice(pathStartIndex);
}

// 获取常规哈希部分，8 字节
function getJsHash(fileName: string): string {
    const len = fileName.length;
    if (len < 12) {
        return ""; // 不检查
    }
    const sep = fileName[len - 12];
    if (sep == "." || sep == "-") {
        return fileName.slice(len - 11, len - 3);
    }
    return ""; // 回退
}

// 根据扩展名获取 MIME 类型
function getMimeTypeByExt(ext: string): string {
    const mapping: { [key: string]: string } = {
        ".html": "text/html",
        ".js": "text/javascript",
        ".wasm": "application/wasm",
        ".css": "text/css",
        ".json": "application/json",
        ".png": "image/png",
        ".jpg": "image/jpeg",
        ".jpeg": "image/jpeg",
        // 添加更多映射
    };
    return mapping[ext.toLowerCase()] || "application/octet-stream";
}

/**
 * 下载并安装文件
 * @param url - 下载 URL
 * @param filePath - 本地路径
 * @param cb - 回调函数
 * @returns
 */
async function downloadFile(url: string, filePath: string, cb: (progress: string) => void = () => { }): Promise<{ statusCode: number, url: string, filePath: string, fileSize: number }> {
    console.log(`下载 ${url} 到 ${filePath}`);
    return new Promise((resolve, reject) => {
        const stream = fs.createWriteStream(filePath);
        const request = net.request(url);
        request.on("response", (response) => {
            let len = 0;
            let cur = 0;
            try {
                const rawLength = response.headers["Content-Length"] || response.headers["content-length"];
                len = parseInt(rawLength || "0");
            } catch (e) {
                len = 0;
                console.error(e);
            }
            console.log(`下载文件预期长度 ${len}`);
            response.on("data", (chunk) => {
                cur += chunk.length;
                stream.write(chunk);
                if (len < 1) {
                    cb((((100 * cur) / 500) * 1024 * 1024).toFixed(2)); // 最大接收 500 MB
                    return;
                }
                if (len < cur) {
                    len = cur;
                }
                const progress = ((100 * cur) / len).toFixed(2);
                cb(progress);
            });
            response.on("end", () => {
                if (response.statusCode == 200) {
                    if (len < 1 || len == cur) {
                        stream.end();
                        resolve({ statusCode: response.statusCode, url, filePath, fileSize: len });
                    } else {
                        stream.end();
                        removeFile(filePath);
                        console.log(`下载文件预期 ${len} 实际 ${cur}`);
                        reject("文件大小不一致，请重新下载。");
                    }
                } else {
                    stream.end();
                    removeFile(filePath);
                    console.log("文件下载错误", response.statusCode);
                    reject(`文件下载错误，状态码为 ${response.statusCode}`);
                }
            });
            response.on("error", (error) => {
                console.error("下载错误", error);
                reject(error);
            });
        });
        request.on("error", (error) => {
            stream.end();
            removeFile(filePath);
            console.error("下载错误 2", error);
            reject(error);
        });
        request.end();
    });
}

/**
 * 获取远程文本
 * @param url - 下载 URL
 * @param headers - 请求头
 * @returns {Promise<string>} - 返回文本
 */
async function getRemoteText(url: string, headers: { [key: string]: string } = {}): Promise<string> {
    return new Promise((resolve, reject) => {
        const request = net.request({
            method: 'GET',
            url,
        });
        // 设置请求头
        Object.keys(headers).forEach(key => {
            request.setHeader(key, headers[key]);
        });

        request.on('response', (response) => {
            let data = '';

            response.on('data', (chunk) => {
                data += chunk;
            });

            response.on('end', () => {
                if (response.statusCode >= 200 && response.statusCode < 300) {
                    resolve(data);
                } else {
                    reject(new Error(`HTTP 错误: ${response.statusCode}`));
                }
            });
        });

        request.on('error', (error) => {
            reject(error);
        });

        request.end();
    });
}

/**
 * 检查文件是否存在
 * @param filePath
 * @returns
 */
function fileExists(filePath: string): boolean {
    try {
        fs.statSync(filePath);
        return true;
    } catch (err) {
        if (err.code === "ENOENT") {
            return false;
        }
        throw err;
    }
}

/**
 * 转换为 SSE 事件
 * @param data - SSE 数据
 * @param event - SSE 事件类型，可选值 complete, progress, current_event_id
 * @returns {string} - SSE 格式字符串
 */
function EventConvertResult(data: string | object, event?: string): string {
    let bufs: string[] = [];
    if (event) {
        bufs.push(`event: ${event}`);
    }
    if (typeof data === "string") {
        bufs.push(`data: ${data}\n\n`);
    } else {
        bufs.push(`data: ${JSON.stringify(data)}\n\n`);
    }

    return bufs.join("\n");
}

/**
 * 获取启动参数
 * @returns [loadUrl, devMode]
 */
function getEnterPoint(): [string, boolean] {
    const argv = process.argv;
    let loadUrl = "app://localhost"; // 默认本地
    let devMode = false; // 默认关闭

    argv.forEach((arg) => {
        if (arg.startsWith("-url=")) {
            loadUrl = arg.split("=", 2)[1];
        } else if (arg.startsWith("-dev")) {
            devMode = true;
        }
    });

    return [loadUrl, devMode];
}

/**
 * 恢复窗口大小和位置
 * @param dir
 * @param mainWindow
 * @param originSize
 */
function restoreWindowSize(dir: string, mainWindow: Electron.BrowserWindow, originSize: { width: number, height: number }): void {
    const confPath = path.join(dir, "window-size.json");
    let bounds;
    try {
        bounds = JSON.parse(fs.readFileSync(confPath, 'utf-8'));
        const { width, height, x, y } = bounds;
        if (width > 100 && height > 100) {
            mainWindow.setSize(width, height);
        }
        if (process.platform === "win32" && x < 1 && y < 1) {
            mainWindow.maximize(); // 设置最大化
            return;
        }

        if (
            width + x > 10 &&
            height + y > 10 &&
            x + 10 < originSize.width &&
            y + 10 < originSize.height
        ) {
            // 确保窗口移动后仍在屏幕内，负偏移大于窗口，正偏移不能超过主窗口大小
            mainWindow.setPosition(x, y);
        }
    } catch (error) {
        console.error(error);
    }
}

/**
 * 保存窗口大小
 * @param dir
 * @param bounds
 */
function saveWindowSize(dir: string, bounds: { width: number, height: number, x: number, y: number }): void {
    const confPath = path.join(dir, "window-size.json");
    try {
        fs.writeFileSync(confPath, JSON.stringify(bounds), 'utf-8');
    } catch (error) {
        console.error(error);
    }
}

/**
 * 获取关闭对话框配置
 * @param dir
 * @returns
 */
function getCloseCfg(dir: string): { isTray: number, noPrompt: number } {
    let ret = {
        isTray: -1, // 是否退出到托盘
        noPrompt: 1, // 如果为 1，默认不显示对话框
    };

    if (process.platform !== "win32") {
        return ret;
    } else {
        ret = {
            isTray: 1, // Windows 默认退出到托盘
            noPrompt: -1, // Windows 默认提示
        };
    }

    const confPath = path.join(dir, "close-cfg.json");
    try {
        ret = JSON.parse(fs.readFileSync(confPath, 'utf-8'));
    } catch (error) {
        console.error(error);
    }

    console.log(ret);

    return ret;
}

/**
 * 保存关闭配置
 * @param dir
 * @param data
 */
function saveCloseCfg(dir: string, data: { isTray: boolean, noPrompt: boolean }): void {
    if (process.platform !== "win32") {
        return; // 无需保存
    }
    const confPath = path.join(dir, "close-cfg.json");
    try {
        const { isTray, noPrompt } = data;
        const ret = { isTray: isTray ? 1 : -1, noPrompt: noPrompt ? 1 : -1 };
        fs.writeFileSync(confPath, JSON.stringify(ret), 'utf-8');
    } catch (e) {
        console.error(e);
    }
}

// Promise 队列
class PromiseQueue {
    private queue: Promise<any> = Promise.resolve(true);
    add(operation: () => Promise<any>): void {
        this.queue = this.queue.then(() => operation()).catch((error) => {
            console.error(error);
        });
    }

    get(): Promise<any> {
        return this.queue;
    }
}

export {
    getExt,
    getBasePath,
    getLastBase,
    getLastPath,
    getMimeTypeByExt,
    downloadFile,
    fileExists,
    EventConvertResult,
    getEnterPoint,
    getJsHash,
    restoreWindowSize,
    saveWindowSize,
    getCloseCfg,
    saveCloseCfg,
    PromiseQueue,
    getRemoteText
};
const utils = {
    getExt,
    getBasePath,
    getLastBase,
    getLastPath,
    getMimeTypeByExt,
    downloadFile,
    fileExists,
    EventConvertResult,
    getEnterPoint,
    getJsHash,
    restoreWindowSize,
    saveWindowSize,
    getCloseCfg,
    saveCloseCfg,
    PromiseQueue,
    getRemoteText
};
export default utils;